package com.masonluo.mlonlinejudge.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.masonluo.mlonlinejudge.dao.*;
import com.masonluo.mlonlinejudge.entity.*;
import com.masonluo.mlonlinejudge.entity.Class;
import com.masonluo.mlonlinejudge.enums.ExamProblemType;
import com.masonluo.mlonlinejudge.enums.ExecResult;
import com.masonluo.mlonlinejudge.exceptions.ResourceNotFoundException;
import com.masonluo.mlonlinejudge.mapper.TaskMapper;
import com.masonluo.mlonlinejudge.model.bo.TaskBo;
import com.masonluo.mlonlinejudge.model.bo.TaskInfoBo;
import com.masonluo.mlonlinejudge.model.bo.TaskWithClassInfoBo;
import com.masonluo.mlonlinejudge.model.dto.JudgeResultDto;
import com.masonluo.mlonlinejudge.model.dto.TaskProblemSituationDto;
import com.masonluo.mlonlinejudge.model.param.*;
import com.masonluo.mlonlinejudge.model.vo.TaskProblemSituationVo;
import com.masonluo.mlonlinejudge.service.TaskService;
import com.masonluo.mlonlinejudge.utils.FastJsonUtil;
import com.masonluo.mlonlinejudge.utils.RedisUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import com.masonluo.mlonlinejudge.result.Result;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static java.sql.Types.NULL;

@Service
public class TaskServicelmpl implements TaskService {

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private TaskRepository taskRepository;

    @Autowired
    private TestPaperRepository testPaperRepository;

    @Autowired
    private ExamSchoolClassRepository examSchoolClassRepository;

    @Autowired
    private ExamAnswerSheetRepository examAnswerSheetRepository;

    @Autowired
    private ExamScoreTaskRepository examScoreTaskRepository;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserClassRepository userClassRepository;

    @Autowired
    private SelectProblemRepository selectProblemRepository;

    @Autowired
    private ResultRepository resultRepository;

    @Autowired
    private ClassRepository classRepository;


    @Override
    public TaskBo createTask(TaskCreateParam task) {

        //考试表插入
        TaskBo taskBo = new TaskBo();
        taskBo.setTitle(task.getTitle());
        taskBo.setDesc(task.getDesc());
        taskBo.setStartTime(new Timestamp(task.getStartTime().getTime()));
        taskBo.setEndTime(new Timestamp(task.getEndTime().getTime()));
        taskBo.setPaperId(task.getPaperId());
        taskBo.setCreatorId(task.getCreatorId());
        //taskBo.setStatus("DISABLE");
        taskRepository.create(taskBo);

        //学校考试关系表插入
        ExamSchoolClass examSchoolClass = new ExamSchoolClass();
        for (int i = 0; i < task.getClassId().length; i++) {
            examSchoolClass.setClassId(task.getClassId()[i]);
            examSchoolClass.setSchoolId(task.getSchoolId());
            examSchoolClass.setExamId(taskBo.getId());
            examSchoolClassRepository.create(examSchoolClass);
        }

        // 创建子线程
        Thread thread = new Thread() {
            @Override
            public void run() {
                //学生考试成绩空记录插入
                ExamScore examScoreTask = new ExamScore();
                examScoreTask.setExamId(taskBo.getId());
                examScoreTask.setTrueFalseQuestionScore(0);
                examScoreTask.setProgramQuestionScore(0);
                examScoreTask.setSingleChoiceScore(0);
                for (Integer classId : task.getClassId()) {
                    for (Integer studentId : userClassRepository.listUserIdsByClassId(classId)) {
                        examScoreTask.setClassId(classId);
                        examScoreTask.setUserId(studentId);
                        examScoreTaskRepository.insertIntoExamScore(examScoreTask);
                    }
                }
            }
        };
        // 启动子线程
        thread.start();


        return taskBo;
    }

    @Override
    public TaskBo findTaskById(Integer id) {

        Task task = taskRepository.selectById(id);
        if (task == null) {
            throw new ResourceNotFoundException(String.format("The exam [%d] does not exist.", id));
        }

        return taskRepository.findTaskById(id);
    }

    @Override
    public Page<TaskBo> findTaskByClassId(Integer pageNum, Integer pageSize, Integer classId) {

        // 获取该班所有考试列表
        QueryWrapper<ExamSchoolClass> examWrapper = new QueryWrapper<ExamSchoolClass>();
        examWrapper.eq("classid", classId);
        //examWrapper.eq("school_id",schoolId);
        List<ExamSchoolClass> examSchoolClasses = examSchoolClassRepository.selectList(examWrapper);
        List<Integer> examIdList = new ArrayList<>(examSchoolClasses.size());
        for (ExamSchoolClass examSchoolClass : examSchoolClasses) {
            examIdList.add(examSchoolClass.getExamId());
        }

        // 根据上面的所有考试列表获取考试
        Page<Task> taskPage;
        Page<Task> Page = new Page<>(pageNum, pageSize);
        if (!examSchoolClasses.isEmpty()) {
            QueryWrapper<Task> taskWrapper = new QueryWrapper<Task>();
            taskWrapper.in("id", examIdList);
            Timestamp now = new Timestamp(System.currentTimeMillis());
            taskWrapper.gt("end_time", now);
            taskWrapper.orderByAsc("start_time");
            taskPage = taskRepository.selectPage(Page, taskWrapper);
        } else {
            throw new ResourceNotFoundException(String.format("This class [%d] does not have a task.", classId));
        }

        // 封装
        List<TaskBo> taskBos = new ArrayList<>();
        for (Task record : taskPage.getRecords()) {
            TaskBo taskBo = new TaskBo();
            BeanUtils.copyProperties(record, taskBo);
            taskBo.setDesc(record.getDescription());
            taskBos.add(taskBo);
        }
        Page<TaskBo> pageTask = new Page<>(pageNum, pageSize);
        pageTask.setTotal(taskPage.getTotal());
        pageTask.setRecords(taskBos);

        return pageTask;
    }

    @Override
    public List<TaskBo> findList(Integer schoolId) {
        List<ExamSchoolClass> examSchoolClass_s = examSchoolClassRepository.selectListBySchoolId(schoolId);
        if (examSchoolClass_s == null) {
            throw new ResourceNotFoundException(String.format("This school [%d] does not have a task.", schoolId));
        }
        List<TaskBo> tasks = new ArrayList<>(examSchoolClass_s.size());
        for (ExamSchoolClass examSchoolClass : examSchoolClass_s) {
            tasks.add(taskRepository.findTaskById(examSchoolClass.getExamId()));
        }
        return tasks;
    }

    @Override
    public Page<TaskWithClassInfoBo> List(Integer pageNum, Integer pageSize, Integer schoolId) {

        //获取school_id相关的exam_id
        List<ExamSchoolClass> examSchoolClasses = examSchoolClassRepository.selectListBySchoolId(schoolId);
        if (examSchoolClasses.size() == 0) {
            throw new ResourceNotFoundException(String.format("This school [%d] does not have a task.", schoolId));
        }
        List<Integer> examIds = new ArrayList<>(examSchoolClasses.size());
        for (ExamSchoolClass examSchoolClass : examSchoolClasses) {
            examIds.add(examSchoolClass.getExamId());
        }

        //获取除classIds以外的属性
        Page<Task> page = new Page<>(pageNum, pageSize);
        QueryWrapper<Task> wrapper = new QueryWrapper<>();
        wrapper.in("id", examIds);
        wrapper.orderByDesc("start_time");
        Page<Task> taskPage = taskRepository.selectPage(page, wrapper);
        if (taskPage == null) {
            throw new ResourceNotFoundException(String.format("This school [%d] does not have a task.", schoolId));
        }
        List<TaskWithClassInfoBo> taskWithClassInfoBos = new ArrayList<>(taskPage.getRecords().size());
        for (Task task : taskPage.getRecords()) {
            taskWithClassInfoBos.add(taskRepository.findTaskWithClassInfoBoById(task.getId()).get(0));
        }

        //获取className and classId
        for (TaskWithClassInfoBo taskWithClassInfoBo : taskWithClassInfoBos) {
            List<ExamSchoolClass> schoolClasses = examSchoolClassRepository.selectListByExamId(taskWithClassInfoBo.getId());
            //classes = new Integer[schoolClasses.size()];
            ArrayList<String> classNamesList = new ArrayList<>(schoolClasses.size());
            ArrayList<Integer> classIdList = new ArrayList<>(schoolClasses.size());
            for (ExamSchoolClass examSchoolClass : schoolClasses) {
                classIdList.add(examSchoolClass.getClassId());
                Class aClass = classRepository.selectById(examSchoolClass.getClassId());
                if (aClass != null) {
                    classNamesList.add(aClass.getClassName());
                } else {
                    classNamesList.add(null);
                }
            }
            taskWithClassInfoBo.setClassNamesList(classNamesList);
            taskWithClassInfoBo.setClassIdList(classIdList);
        }

        Page<TaskWithClassInfoBo> taskWithClassInfoBoPage = new Page<>(pageNum, pageSize);
        taskWithClassInfoBoPage.setRecords(taskWithClassInfoBos);
        taskWithClassInfoBoPage.setTotal(taskPage.getTotal());
        return taskWithClassInfoBoPage;
    }

    @Override
    public boolean judgeTimeout(Integer examId) {
        TaskBo bo = taskRepository.findTaskById(examId);
        Date now = new Date();
        if (now.after(bo.getStartTime()) && now.before(bo.getEndTime())) {
            return false;
        }
        return true;
    }

    @Override
    public boolean judgeExist(SubmitParam submitParam) {
        Integer exist = examAnswerSheetRepository.judgeExist(submitParam);
        if (!ObjectUtils.isEmpty(exist)) {
            //当存在时
            return true;
        } else {
            //当不存在
            return false;
        }
    }

    @Override
    public void delete(Integer id) {
        TaskBo taskBo = taskRepository.findTaskById(id);
        Date now = new Date();
        if (now.after(taskBo.getStartTime()) && now.before(taskBo.getEndTime())) {
            throw new IllegalArgumentException("考试进行中,无法删除答卷");
        }
        taskRepository.deleteById(id);
        List<ExamSchoolClass> examSchoolClasses_s = examSchoolClassRepository.selectListByExamId(id);
        for (ExamSchoolClass examSchoolClass : examSchoolClasses_s) {
            examSchoolClassRepository.deleteById(examSchoolClass.getId());
        }
    }

    @Override
    public TaskBo update(EditTaskParam editTaskParam) {
        if (taskRepository.updateTask(editTaskParam) > 0) {
            return new TaskBo(
                    editTaskParam.getId(),
                    editTaskParam.getTitle(),
                    editTaskParam.getDesc(),
                    editTaskParam.getStartTime(),
                    editTaskParam.getEndTime(),
                    editTaskParam.getPaperId(),
                    //editTaskParam.getStatus(),
                    editTaskParam.getCreatorId());
        }
        throw new ResourceNotFoundException("Fail to update the exam entity, please confirm the Task exist");
    }

    @Override
    public void updateTime(TaskChangeTimeParam taskChangeTimeParam) {

        Task task = taskRepository.selectById(taskChangeTimeParam.getId());
        EditTaskParam editTaskParam = new EditTaskParam();

        editTaskParam.setId(task.getId());
        editTaskParam.setDesc(task.getDescription());
        editTaskParam.setTitle(task.getTitle());
        editTaskParam.setPaperId(task.getPaperId());
        //editTaskParam.setStatus(task.getStatus());
        editTaskParam.setCreatorId(task.getCreatorId());
        editTaskParam.setStartTime(new Timestamp(taskChangeTimeParam.getStartTime().getTime()));
        editTaskParam.setEndTime(new Timestamp(taskChangeTimeParam.getEndTime().getTime()));
        update(editTaskParam);
    }

    @Override
    public TaskInfoBo editTask(ChangeTaskParam changeTaskParam) {

        EditTaskParam editTaskParam = new EditTaskParam();

        editTaskParam.setId(changeTaskParam.getId());
        editTaskParam.setDesc(changeTaskParam.getDesc());
        editTaskParam.setTitle(changeTaskParam.getTitle());
        editTaskParam.setPaperId(changeTaskParam.getPaperId());
//        editTaskParam.setStatus(changeTaskParam.getStatus());
        editTaskParam.setCreatorId(changeTaskParam.getCreatorId());
        editTaskParam.setStartTime(new Timestamp(changeTaskParam.getStartTime().getTime()));
        editTaskParam.setEndTime(new Timestamp(changeTaskParam.getEndTime().getTime()));

        TaskBo taskBo = update(editTaskParam);


        List<ExamSchoolClass> examSchoolClasses = examSchoolClassRepository.selectListByExamId(changeTaskParam.getId());
        if (examSchoolClasses.size() != 0) {
            if (!examSchoolClasses.get(0).getSchoolId().equals(changeTaskParam.getSchoolId())) {
                throw new IllegalArgumentException("不可更改考试所属学校");
            }
            for (ExamSchoolClass examSchoolClass : examSchoolClasses) {
                examSchoolClassRepository.deleteByExamId(examSchoolClass.getExamId());
            }
        }
        for (Integer classId : changeTaskParam.getClassIds()) {
            ExamSchoolClass examSchoolClass = new ExamSchoolClass();
            examSchoolClass.setExamId(changeTaskParam.getId());
            examSchoolClass.setClassId(classId);
            examSchoolClass.setSchoolId(changeTaskParam.getSchoolId());
            examSchoolClassRepository.insert(examSchoolClass);
        }

        TaskInfoBo taskInfoBo = new TaskInfoBo();

        taskInfoBo.setClassIds(changeTaskParam.getClassIds());
        taskInfoBo.setId(taskBo.getId());
        taskInfoBo.setDesc(taskBo.getDesc());
        taskInfoBo.setTitle(taskBo.getTitle());
        taskInfoBo.setStartTime(taskBo.getStartTime());
        taskInfoBo.setEndTime(taskBo.getEndTime());
        taskInfoBo.setPaperId(taskBo.getPaperId());
        //taskInfoBo.setStatus(task.getStatus());
        taskInfoBo.setCreatorId(taskBo.getCreatorId());
        return taskInfoBo;
    }


//    @Override
//    public TaskBo pushExam(TaskChangeStatusParam taskChangeStatusParam) {
//
//        Task task = taskRepository.selectById(taskChangeStatusParam.getId());
//
//        EditTaskParam editTaskParam = new EditTaskParam();
//
//        editTaskParam.setId(task.getId());
//        editTaskParam.setDesc(task.getDescription());
//        editTaskParam.setTitle(task.getTitle());
//        editTaskParam.setStartTime(task.getStartTime());
//        editTaskParam.setEndTime(task.getEndTime());
//        editTaskParam.setPaperId(task.getPaperId());
//        editTaskParam.setCreatorId(task.getCreatorId());
//        editTaskParam.setStatus(taskChangeStatusParam.getStatus());
//
//        Date current = new Date();
//        if ((task.getStartTime().compareTo(current)>0 || task.getEndTime().compareTo(current)<0) &&
//        taskChangeStatusParam.getStatus().equals("ENABLE")){
//            throw new IllegalArgumentException("当前未在考试时间段，无法发布");
//        }
//        return update(editTaskParam);
//    }

    @Override
    public TaskInfoBo getTaskInfo(Integer taskId) {
        TaskInfoBo taskInfoBo = new TaskInfoBo();

        Task task = taskRepository.selectById(taskId);

        taskInfoBo.setId(task.getId());
        taskInfoBo.setDesc(task.getDescription());
        taskInfoBo.setTitle(task.getTitle());
        taskInfoBo.setStartTime(task.getStartTime());
        taskInfoBo.setEndTime(task.getEndTime());
        taskInfoBo.setPaperId(task.getPaperId());
        //taskInfoBo.setStatus(task.getStatus());
        taskInfoBo.setCreatorId(task.getCreatorId());

        List<ExamSchoolClass> examSchoolClasses = examSchoolClassRepository.selectListByExamId(taskId);

        taskInfoBo.setSchoolId(examSchoolClasses.get(0).getSchoolId());
        //List<Integer> classId = new ArrayList<>();
        Integer[] classIds = new Integer[examSchoolClasses.size()];

        int i = 0;
        for (ExamSchoolClass examSchoolClass : examSchoolClasses) {
            //classId.add(examSchoolClass.getClassId());
            classIds[i] = examSchoolClass.getClassId();
            i++;
        }

        taskInfoBo.setClassIds(classIds);

        return taskInfoBo;
    }

    @Override
    public void submit(SubmitParam submitParam) {
        // 获取选择和填空的内容
        List<AnswerContent> answerContents = submitParam.getAnswerContent();
        List<Object> solutionList = redisUtil.lGet(submitParam.getUserId().toString(), 0, -1);
        List<JudgeResultDto> judgeResultDtoList = JSON.parseArray(solutionList.toString(), JudgeResultDto.class);

        List<JudgeResultDto> resultList = filterRedisList(judgeResultDtoList, submitParam);

//            计算答卷得分，修改编程题答卷内容
        Integer single_chioce_score = 0;
        Integer true_false_question_score = 0;
        Integer program_question_score = 0;
        for (int i = 0; i < answerContents.size(); i++) {
            AnswerContent answerContent = answerContents.get(i);
//            如果是编程题
            if (answerContent.getExamProblemType().equals(ExamProblemType.PROGRAMMING.id)) {
                Integer problemId = answerContent.getProblemId();
                for (int j = 0; j < resultList.size(); j++) {
                    JudgeResultDto dto = resultList.get(j);
                    if (problemId.equals(dto.getProblemId()) && dto.isCompilerResult() == true && dto.getProcessResult().equals(ExecResult.SUCCESS)) {
                        program_question_score += answerContent.getScore();
                        answerContent.setResult(1);
                        break;
                    }
                }
            } else {
//                如果是选择或者判断题并且正确了
                String answer = selectProblemRepository.getProblemAnswer(answerContent.getProblemId());
                if (answerContent.getAnswer().equals(answer)) {
                    answerContent.setResult(1);
                    Integer score = answerContent.getScore();
                    if (answerContent.getExamProblemType().equals(ExamProblemType.CHOICE.id)) {
                        single_chioce_score += score;
                    } else if (answerContent.getExamProblemType().equals(ExamProblemType.TRUEORFALSE.id)) {
                        true_false_question_score += score;
                    }
                }
            }
        }
//        存储答卷
        ExamAnswerSheet examAnswerSheet = new ExamAnswerSheet();
        examAnswerSheet.setUserId(submitParam.getUserId());
        examAnswerSheet.setExamId(submitParam.getExamId());
        examAnswerSheet.setClassId(submitParam.getClassId());
        examAnswerSheet.setAnswer_content(FastJsonUtil.getJSONString(answerContents));
        examAnswerSheetRepository.insertIntoExamAnswerSheet(examAnswerSheet);
//        存储得分
        ExamScore examScore = new ExamScore();
        examScore.setAnswerSheetId(examAnswerSheet.getId());
        examScore.setExamId(submitParam.getExamId());
        examScore.setClassId(examAnswerSheet.getClassId());
        examScore.setUserId(examAnswerSheet.getUserId());
        examScore.setSingleChoiceScore(single_chioce_score);
        examScore.setTrueFalseQuestionScore(true_false_question_score);
        examScore.setProgramQuestionScore(program_question_score);
        examScoreTaskRepository.updateExamScore(examScore);
    }

    public List<JudgeResultDto> filterRedisList(List<JudgeResultDto> judgeResultDtoList, SubmitParam submitParam) {
        List<JudgeResultDto> resultList = new ArrayList<>();
//        筛选出本次考试的List
        for (int i = 0; i < judgeResultDtoList.size(); i++) {
            JudgeResultDto dto = judgeResultDtoList.get(i);
            if (submitParam.getExamId().equals(dto.getExamId())) {
                resultList.add(dto);
            }
        }
//        筛选出每题一个记录，如果都为错误，则保留最后一条，如果有一条成功，则保留成功
        for (int i = resultList.size() - 1; i >= 0; i--) {
            JudgeResultDto dto1 = resultList.get(i);
            Integer problemId = dto1.getProblemId();
            if (dto1.isCompilerResult() == true && dto1.getProcessResult().equals(ExecResult.SUCCESS)) {
                for (int j = 0; j < i; j++) {
                    JudgeResultDto dto2 = resultList.get(j);
                    if (dto2.getProblemId().equals(problemId)) {
                        resultList.remove(j);
                        j -= 1;
                        i -= 1;
                    }
                }
            } else {
                for (int j = 0; j < i; j++) {
                    JudgeResultDto dto3 = resultList.get(j);
                    if (dto3.getProblemId().equals(problemId)) {
                        if (dto3.isCompilerResult() == true && dto3.getProcessResult().equals(ExecResult.SUCCESS)) {
                            resultList.remove(i);
                            break;
                        } else {
                            resultList.remove(j);
                            j -= 1;
                            i -= 1;
                        }
                    }
                }
            }
        }
        return resultList;
    }

    @Override
    public List<AnswerContent> queryAnswerContent(Integer userId, Integer examId) {

        List<ExamAnswerSheet> examAnswerSheets = examAnswerSheetRepository.queryExamAnswerSheet(userId, examId);

        ArrayList<AnswerContent> answerContents = new ArrayList<>();

        if (!examAnswerSheets.isEmpty()) {
            for (ExamAnswerSheet examAnswerSheet : examAnswerSheets) {
                ArrayList<AnswerContent> contentArrayList = FastJsonUtil.getList(examAnswerSheet.getAnswer_content(), AnswerContent.class);
                for (AnswerContent answerContent : contentArrayList) {
                    answerContents.add(answerContent);
                }
            }
        }
        return answerContents;
    }

    @Override
    public Result storeExamAnswerSheet(SubmitParam submitParam) {
//        不管存不存在缓存，先删除该key的Redis记录
        redisUtil.del("Exam" + submitParam.getExamId() + "AnswerSheet" + submitParam.getUserId());
//        再存储到Redis，缓存4小时
        redisUtil.set("Exam" + submitParam.getExamId() + "AnswerSheet" + submitParam.getUserId(), submitParam, 14400);
        return Result.ok();
    }

    @Override
    public Result getExamAnswerSheetCache(Integer userId, Integer examId) {
        return Result.ok(redisUtil.get("Exam" + examId + "AnswerSheet" + userId));
    }

    @Override
    public List<TaskProblemSituationVo> queryExamProblemResponseSituation(Integer userId, Integer examId) {

        List<TaskProblemSituationVo> taskProblemSituationVoList = new ArrayList<TaskProblemSituationVo>();

        //读取该场考试所有的编程题
        List<Integer> programmingQuestionsIdList = testPaperRepository.queryProgrammingQuestionsByExamId(examId);

        //读取该次查询的所有已提交编程题
        List<TaskProblemSituationDto> submittedList = taskRepository.queryTaskProblemSituation(userId, examId);

        for (Integer integer : programmingQuestionsIdList) {

            TaskProblemSituationVo taskProblemSituationVo = new TaskProblemSituationVo();

            boolean submitted = false;

            for (TaskProblemSituationDto taskProblemSituationDto : submittedList) {
                if (taskProblemSituationDto.getProblemId().equals(integer)) {
                    com.masonluo.mlonlinejudge.entity.Result result = resultRepository.selectById(taskProblemSituationDto.getResultId());
                    if (result.isCompilerResult() && result.getProcessResult().getId() == 0) {
                        taskProblemSituationVo.setSituationInfo("已通过");
                    } else {
                        taskProblemSituationVo.setSituationInfo("未通过");
                    }
                    submitted = true;
                }
            }
            if (!submitted) taskProblemSituationVo.setSituationInfo("未提交");

            taskProblemSituationVo.setProblemId(integer);

            taskProblemSituationVoList.add(taskProblemSituationVo);
        }
        return taskProblemSituationVoList;
    }

}
